package guowei.gulistorage.storage.config;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

@Configuration
public class MessageConvertConfig {

    @Autowired
    private RabbitTemplate rabbitTemplate;

//自定义rabbitMq发送消息的转换器 将发送的消息转换为json
    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }

//  定制rabbirTemplate组件，设置消息确认机制 1.ConfirmCallback  2.returnCallback  3.ack


    /*  定制RabbitTemplate
        1.服务收到信息就回调
            ①. spring.rabbitmq.publisher-confirm-type: correlated
            ②. 设置确认回调ConfirmCallback

        2.消息正确抵达队列进行回调
            ①. spring.rabbitmq.publisher-returns: true
                spring.rabbitmq.template.mandatory: true
            ②. 设置确认回调ReturnCallback

        3. 消费端确认(保证每个消息被正确消费，此时broker才可以删除信息)
            spring.rabbitmq.listener.simple.acknowledge-mode: manual
            ①. 默认是自动确认的，只要消息接收到，客户端会自动确认，服务端会移除这个消息
                有bug 我们收到很多信息，直接自动回复给broker,但服务器ack正在处理时，正好宕机，会发生信息丢失，就是回复成功的没处理成功
                因此我们需要手动确认信息。
            ②. 如何签收
                 channel.basicAck(deliveryTag,false) 签收，业务完成就应该签收
                 channel.basicNack(deliveryTag,false,true) 拒签，业务失败。  第二个true表示重新入队,false表示丢掉信息


     */

    @PostConstruct      //rabbitTemplate对象创建完成之后，再调用此方法
    public void initRabbitTempaltee(){
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             *
             * @param correlationData   当前关联消息的唯一关联数据(消息的唯一id)
             * @param b     消息发送broker服务器是否成功
             * @param s     消息发送失败的原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                if (b){
                    System.out.println("是否成功"+b+"       消息发送失败原因"+s+"   correlationData为"+correlationData);
                }else {
                    System.out.println("消息发送失败   "+"     是否成功"+b+"       消息发送失败原因  "+s+"   correlationData为  "+correlationData);
                }
            }
        });


        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             *  只要交换机没有将消息投递指定队列，就触发这个失败回调
             * @param message    投递失败的消息详情信息
             * @param i     回复的状态码
             * @param s     回复的文本信息
             * @param s1    这个消息发送给那个交换机
             * @param s2    用的那个路由键
             */
            @Override
            public void returnedMessage(Message message, int i, String s, String s1, String s2) {
                System.out.println("消息投递队列失败  "+"  那个路由键  "+s2+"  那个交换机 "+s1+"  回复的文本信息  "+s);
            }
        });
    }

}
